import 'package:ddstore/dd_notifier.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

class DDStoreValue<T> {
  T? oldValue;
  ValueNotifier<T?>? newValue;
  DDStoreValue({
    this.oldValue,
    this.newValue,
  });
}

class DDBuilder<T> extends StatefulWidget {
  final String valueKey;
  final ValueWidgetBuilder<T?> builder;
  final Widget? child;
  final T? defaultValue;
  final DDStore? store;
  const DDBuilder({
    Key? key,
    required this.valueKey,
    required this.builder,
    this.defaultValue,
    this.store,
    this.child,
  }) : super(key: key);

  @override
  State<DDBuilder> createState() => _DDBuilderState<T>();
}

class _DDBuilderState<T> extends State<DDBuilder<T>> with DDStoreMixin {
  @override
  Widget build(BuildContext context) {
    final store = widget.store ?? $store;
    return ValueListenableBuilder<T?>(
      valueListenable: store.notifierForKey<T>(widget.valueKey, defaultValue: widget.defaultValue),
      builder: widget.builder,
      child: widget.child,
    );
  }
}

class DDStore {
  /// widget之间共享数据的方式,
  final Map<String, DDStoreValue> _storeValues = {};
  DDNotifier? _notifier;
  DDStore() : _notifier = DDNotifier();

  /// 用于widget自动生成notifier
  ValueNotifier<K?> notifierForKey<K>(String key, {K? defaultValue}) {
    final item = _storeValues[key]?.newValue;
    if (item == null) {
      final notifier = ValueNotifier<K?>(defaultValue);
      _storeValues[key] = DDStoreValue<K>(oldValue: null, newValue: notifier);
      return notifier;
    }
    assert(item is ValueNotifier<K?>, "当前key:$key 和 类型对不上啊啊");
    return item as ValueNotifier<K?>;
  }

  K? valueForKey<K>(String key) {
    final item = _storeValues[key]?.newValue?.value;
    assert(item is K?, "当前key:$key 和 类型对不上啊啊");
    return item as K?;
  }

  void setValue<K>({required String key, required K value}) {
    final currentValue = _storeValues[key];
    if (value == currentValue?.newValue?.value) {
      debugPrint("新值和旧值相同，无需替换");
      return;
    }
    currentValue?.oldValue = currentValue.newValue?.value;
    currentValue?.newValue?.value = value;
  }

  dynamic emit(
    String name, {
    DDActionFutureType type = DDActionFutureType.none,
    List<dynamic>? positionalArguments,
    Map<Symbol, dynamic>? namedArguments,
  }) {
    return _notifier?.dispatch(
      name,
      type: type,
      positionalArguments: positionalArguments,
      namedArguments: namedArguments,
    );
  }

  void _dispose() {
    _storeValues.forEach((key, value) {
      value.newValue?.dispose();
    });
    _storeValues.clear();
    _notifier?.dispose();
  }
}

mixin DDStoreMixin<T extends StatefulWidget> on State<T> {
  DDStore? _store;
  bool _isRootStore = false;

  @nonVirtual
  DDStore get $store {
    assert(_store != null, "store 还未初始化，请检查");
    return _store!;
  }

  List<DDAction> get storeActions => [];
  List<DDAction> _storeActions = [];

  @override
  void initState() {
    super.initState();
    if (forceRootStore) {
      _isRootStore = true;
      _store = DDStore();
    }
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    _initStore();
  }

  @override
  void dispose() {
    if (_isRootStore) {
      _store?._dispose();
    } else {
      for (var element in _storeActions) {
        if (element.callback != null) {
          $store._notifier?.removeListener(
            name: element.name,
            callback: element.callback!,
          );
        }
      }
    }
    super.dispose();
  }

  @nonVirtual
  _initStore() {
    if (_store != null) {
      return;
    }
    final state = context.findAncestorStateOfType<DDStoreMixin>();
    if (state != null) {
      _isRootStore = false;
      _store = state.$store;
    } else {
      _isRootStore = true;
      _store ??= DDStore();
    }
    _storeActions = storeActions;
    for (var element in _storeActions) {
      if (element.callback != null) {
        $store._notifier?.addListener(
          name: element.name,
          callback: element.callback!,
        );
      }
    }
  }

  /// 是否强制当前widget 为rootStore
  @protected
  bool get forceRootStore {
    return false;
  }
}
